#******************************************************************************
#******************************************************************************

from ..common import formulas as core

from ..pipes import fic

from dataclasses import dataclass, field #, InitVar

import numpy as np
        
#******************************************************************************
#******************************************************************************
#******************************************************************************
#******************************************************************************
#******************************************************************************

@dataclass
class PipeUnderForcedInternalCirculation:
    """A class for pipes under forced internal circulation."""
    pipe: fic.InsulatedPipe
    fluid_inlet: fic.Fluid 
    fluid_db: fic.FluidDatabase
    # One among the following two fields should not be None:
    temperature_surroundings: float = None # Kelvins
    thermal_resistance_to_surroundings: float = None # surroundings = pipe
    pipe_heat_flux: float = None
    # One among the following three fields should not be None:
    fluid_speed: float = None
    volumetric_flow_rate: float = None
    mass_flow_rate: float = None
    # optional
    bulk_temperature: float = None # Kelvins
    bulk_temperature_tolerance: float = 1.0 # Kelvins
    max_number_iterations: int = 5
    # to be computed:
    heat_transfer_condition: str = field(init=False)
    outlet_temperature: float = field(init=False)
    fluid_bulk: fic.Fluid = field(init=False)
    fluid_outlet: fic.Fluid = field(init=False)
    reynolds_number: float = field(init=False)
    flow_regime: str = field(init=False)
    friction_factor: float = field(init=False)
    specific_pressure_drop: float = field(init=False)
    pressure_drop: float = field(init=False)
    nusselt_number: float = field(init=False)
    thermal_resistance: float = field(init=False)
    heat_transfer_coefficient: float = field(init=False)
    pipe_efficiency: float = field(init=False)
    pumping_power: float = field(init=False)
    heat_transfer_rate: float = field(init=False)
    # temperature_drop: float = field(init=False)
    # h_convection: float = field(init=False)
    # heat_loss_coefficient: float = field(init=False)

    def __post_init__(self):
        
        #**********************************************************************
        
        # determine the heat transfer condition
        
        if (self.pipe_heat_flux == None and
            self.temperature_surroundings != None and 
            self.thermal_resistance_to_surroundings != None):
            
            # the thermal resistance must be positive
            
            if self.thermal_resistance_to_surroundings < 0:
                
                # incorrect inputs
                
                return -1
            
            # and the temperature of the surroundings too ( kelvins)
            
            if self.temperature_surroundings < 0:
                
                # incorrect inputs
                
                return -1
            
            # constant surface temperature
            
            self.heat_transfer_condition = fic.CONDITION_ISOTHERMAL_SURFACE
            
        elif (self.temperature_surroundings == None and
              self.thermal_resistance_to_surroundings == None and
              self.pipe_heat_flux != None):
    
            # constant heat flux
            
            self.heat_transfer_condition = fic.CONDITION_CONSTANT_HEAT_FLOW
            
            # the heat flux must be finite but can be positive or negative
            
        else: 
            
            # incorrect inputs
            
            return -1
        
        #**********************************************************************
        
        # determine bulk temperature to assess fluid properties
        
        if self.bulk_temperature == None:
            
            # the bulk temperature has not been provided

            #******************************************************************
            
            # is the fluid being heated or cooled?
            
            if self.heat_transfer_condition == fic.CONDITION_ISOTHERMAL_SURFACE:
            
                # if the constant surface temperature condition is valid
            
                # are the surroundings are colder than the fluid at the inlet?
            
                if (self.fluid_inlet.temperature > 
                    self.temperature_surroundings):
            
                    # if so, the fluid is being cooled
                    
                    # then, guess the outlet temperature accordingly
                
                    self.outlet_temperature = (
                        self.fluid_inlet.temperature-
                        self.bulk_temperature_tolerance/2)
                    
                else:
                    
                    # if not, the fluid is being cooled
                    
                    # then, guess the outlet temperature accordingly
                    
                    self.outlet_temperature = (
                        self.fluid_inlet.temperature
                        +self.bulk_temperature_tolerance/2)
            
            else: 
                
                # self.heat_transfer_condition == CONDITION_CONSTANT_HEAT_FLOW:
                    
                # if the constant surface temperature condition is not valid and 
                # instead the constant heat flux condition is valid
                
                if self.pipe_heat_flux >= 0:
                
                    # if the heat flux is positive, the fluid is being heated
                
                    # guess the outlet temperature accordingly
                    
                    self.outlet_temperature = (
                        self.fluid_inlet.temperature+
                        self.bulk_temperature_tolerance/2)
                    
                else:
                
                    # if the heat flux is negative, the fluid is being cooled
                
                    # guess the outlet temperature accordingly
                    
                    self.outlet_temperature = (
                        self.fluid_inlet.temperature-
                        self.bulk_temperature_tolerance/2)
                    
                #**************************************************************
                
            self.bulk_temperature = (self.outlet_temperature+
                                     self.fluid_inlet.temperature)/2
                
            #******************************************************************
            
        else: # the bulk temperature has been provided
            
            # check if it makes sense
            
            if self.bulk_temperature < 0:
                
                print('Temperatures in Kelvin cannot be negative.')
                
                return -1
            
        #**********************************************************************
        
        # iterate until ...
        
        iteration = 0
        
        while iteration < self.max_number_iterations:
            
            #******************************************************************
            
            # define bulk fluid
            
            self.fluid_bulk = fic.Fluid(phase='l',
                                        temperature=self.bulk_temperature,
                                        pressure=self.fluid_inlet.pressure,
                                        db=self.fluid_db)

            #******************************************************************
            
            # calculate relevant flow regime variables
            
            (self.fluid_speed, 
             self.mass_flow_rate, 
             self.volumetric_flow_rate) = fic.calculateFlowRegimeTuple(
                 self.pipe, 
                 self.fluid_bulk,
                 fluid_speed=self.fluid_speed,
                 volumetric_flow_rate=self.volumetric_flow_rate,
                 mass_flow_rate=self.mass_flow_rate)
                        
            #******************************************************************
            
            # reynolds number
            
            self.reynolds_number = core.ReynoldsNumber(
                self.pipe.d_int, 
                self.fluid_speed, 
                self.fluid_bulk.kinematic_viscosity)
            
            #******************************************************************
            
            # flow regime
            
            self.flow_regime = fic.FlowRegime(self.reynolds_number)
       
            #******************************************************************
            
            # darcy friction factor
            
            self.friction_factor = fic.DarcyFrictionFactor(
                self.reynolds_number,
                self.pipe,
                self.flow_regime)
                                                  
            #******************************************************************
            
            # specific pressure drop
            
            self.specific_pressure_drop = fic.SpecificPressureLossInPipe(
                self.fluid_bulk.mass_density, 
                self.pipe.d_int, 
                self.fluid_speed, 
                self.friction_factor)
           
            #******************************************************************
            
            # pressure drop
            
            self.pressure_drop = self.specific_pressure_drop*self.pipe.length
       
            #******************************************************************
            
            # pumping power
            
            self.pumping_power = fic.MechanicalPumpingPower(
                self.volumetric_flow_rate, self.pressure_drop)
           
            #******************************************************************
            
            # nusselt number
            
            self.nusselt_number = fic.NusseltNumber(
                self.pipe, 
                self.friction_factor, 
                self.reynolds_number, 
                self.fluid_bulk.prandtl_number,
                self.flow_regime)
            
            #******************************************************************
            
            # self.heat_transfer_coefficient = (
            #     self.nusselt_number*
            #     self.fluid_bulk.thermal_conductivity/
            #     self.pipe.d_int
            #     )
            
            # h = U/A = 1/R*A
            
            h_int = (
                self.nusselt_number*
                self.fluid_bulk.thermal_conductivity/
                self.pipe.d_int
                )
            
            h_ground = np.inf
            
            # 
            
            if self.heat_transfer_condition == fic.CONDITION_CONSTANT_HEAT_FLOW:
            
                self.thermal_resistance = fic.InsulatedPipeSpecificResistance(
                    h_int,
                    h_ground,
                    0,
                    self.pipe)/self.pipe.length
            
            else:
            
                self.thermal_resistance = fic.InsulatedPipeSpecificResistance(
                    h_int,
                    h_ground,
                    self.thermal_resistance_to_surroundings*self.pipe.length,
                    self.pipe)/self.pipe.length
            
            # pipe area
            
            self.heat_transfer_coefficient = (
                1/(self.thermal_resistance*self.pipe.InternalSurfaceArea())
                )
           
            #******************************************************************
            
            # steady state pipe efficiency and outlet temperature
            
            (self.pipe_efficiency, 
             self.outlet_temperature,
             self.heat_transfer_rate) = fic.PipeHeatTransferCalculations(
                 self.pipe,
                 self.fluid_inlet,
                 self.fluid_bulk,
                 self.fluid_speed,
                 self.heat_transfer_condition,
                 self.temperature_surroundings,
                 self.thermal_resistance,
                 self.pipe_heat_flux)
            
            # compute the new bulk temperature
            
            self.bulk_temperature = (self.fluid_inlet.temperature+
                                     self.outlet_temperature)/2
                        
            # check how it compares with the original one
            
            if (abs(self.bulk_temperature-self.fluid_bulk.temperature) <
                self.bulk_temperature_tolerance):
                
                # break out of the loop
                
                break
            
            # increment iteration counter
            
            iteration = iteration + 1

        #**********************************************************************
        
        self.fluid_outlet = fic.Fluid(phase='l',
                                      temperature=self.outlet_temperature,
                                      pressure=self.fluid_inlet.pressure,
                                      db=self.fluid_db)

        #**********************************************************************
        
        # # heat flow rate (positive if there are heat losses)
        
        # self.heat_transfer_rate = HeatTransferRateInPipe(self.fluid_speed, 
        #                                                  self.fluid_inlet, 
        #                                                  self.fluid_outlet, 
        #                                                  self.pipe)
        
        #**********************************************************************

#******************************************************************************
#******************************************************************************
#******************************************************************************
#******************************************************************************